package javaer.starter.idea.util.sql;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetEncoder;

/**
 * sql转义
 *
 * @author yangzhen
 * @date 2021/12/29 16:05
 * @since 1.0.0
 */
public class SQLEscape {


    /**
     * @param content
     * @param stringLength
     * @return
     * @see com.mysql.cj.ClientPreparedQueryBindings#setString(int, String)
     */
    private static boolean isEscapeNeededForString(String content, int stringLength) {
        boolean needsHexEscape = false;

        for (int i = 0; i < stringLength; ++i) {
            char c = content.charAt(i);

            switch (c) {
                case 0: /* Must be escaped for 'mysql' */
                case '\n': /* Must be escaped for logs */
                case '\r':
                case '\\':
                case '\'':
                case '"': /* Better safe than sorry */
                case '\032': /* This gives problems on Win32 */
                    needsHexEscape = true;
                    break;
            }

            if (needsHexEscape) {
                break; // no need to scan more
            }
        }
        return needsHexEscape;
    }

    public static String escape(String context) {

        if (context == null) {
            return null;
        }

        CharsetEncoder charsetEncoder = null;
        int stringLength = context.length();
        if (isEscapeNeededForString(context, stringLength)) {

            StringBuilder buf = new StringBuilder((int) (context.length() * 1.1));
            buf.append('\'');
            //
            // Note: buf.append(char) is _faster_ than appending in blocks, because the block append requires a System.arraycopy().... go figure...
            //
            for (int i = 0; i < stringLength; ++i) {
                char c = context.charAt(i);

                switch (c) {
                    case 0: /* Must be escaped for 'mysql' */
                        buf.append('\\');
                        buf.append('0');
                        break;
                    case '\n': /* Must be escaped for logs */
                        buf.append('\\');
                        buf.append('n');
                        break;
                    case '\r':
                        buf.append('\\');
                        buf.append('r');
                        break;
                    case '\\':
                        buf.append('\\');
                        buf.append('\\');
                        break;
                    case '\'':
                        buf.append('\\');
                        buf.append('\'');
                        break;
                    case '"': /* Better safe than sorry */
                        buf.append('"');
                        break;
                    case '\032': /* This gives problems on Win32 */
                        buf.append('\\');
                        buf.append('Z');
                        break;
                    case '\u00a5':
                    case '\u20a9':
                        // escape characters interpreted as backslash by mysql
                        if (charsetEncoder != null) {
                            CharBuffer cbuf = CharBuffer.allocate(1);
                            ByteBuffer bbuf = ByteBuffer.allocate(1);
                            cbuf.put(c);
                            cbuf.position(0);
                            charsetEncoder.encode(cbuf, bbuf, true);
                            if (bbuf.get(0) == '\\') {
                                buf.append('\\');
                            }
                        }
                        buf.append(c);
                        break;

                    default:
                        buf.append(c);
                }
            }

            buf.append('\'');
            return buf.toString();
        } else {
            StringBuilder strBuilder = new StringBuilder(stringLength + 2);
            strBuilder.append('\'');
            strBuilder.append(context);
            strBuilder.append('\'');
            return strBuilder.toString();
        }
    }
}
